import time

from .models import SaltMaster
from .models import SaltAuth
from .models import Host
from datetime import datetime
from rest_framework.exceptions import AuthenticationFailed
import json
import requests
import logging
from urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# 获得logger实例
logger = logging.getLogger('django')
class SaltApi:
    '''

    '''
    def __init__(self):
        '''
        saltapi 接口参数初始化
        :param url: saltapi 地址
        :param username: saltapi 用户名
        :param password: saltapi 密码
        :param expire: saltapi 过期时间
        :param token: saltapi token
        '''
        saltmaster = SaltMaster.objects.filter(saltm_status=1).first()
        if saltmaster is not None:
            self.id = saltmaster.id
            self.url = saltmaster.saltm_url
            self.username = saltmaster.saltm_user
            self.password = saltmaster.saltm_passwd
            self.headers = {
                "Content-type": "application/json"
            }

            self.expire = saltmaster.saltm_expire
            self.token = saltmaster.saltm_token
            self.login_url = self.url + "login"
            self.login_params = {'username': self.username, 'password': self.password, 'eauth': 'pam'}
            #过期时间和当前时间差值小于30s
            if len(self.token) == 0 or len(str(self.expire)) == 0 or (int(self.expire) - datetime.now().timestamp() < 30) :
                #重新获取token
                resource = self._get_salt_token()
                mess = '重新获取salt_tokenk结果：%s'%resource
                logger.info(mess)
                if resource['errcode'] != 200:
                    logger.error(resource)
                    #抛出401认证失败
                    raise  AuthenticationFailed("重新获取saltapi token失败")

            self.headers = {
                'X-Auth-Token': self.token,
                "Content-type": "application/json"
            }

        else:
            # 抛出401认证失败
            raise AuthenticationFailed("获取saltmaster 信息失败")



    def _get_salt_token(self):
        '''
        获取 saltapi token
        :return:
        '''
        try:
            post_get_data = self._get_data(self.login_url, self.login_params)

            if post_get_data['errcode'] == 200:
                self.token = post_get_data['mess']['token']
                self.expire = int(str(post_get_data['mess']['expire']).split('.')[0])
                #更新数据库中的token 和expire信息
                SaltMaster.objects.filter(id=self.id).update(saltm_token=self.token,saltm_expire=self.expire,saltm_update=datetime.now().timestamp())

                data = {"errcode": 200, "token": self.token}
            elif post_get_data['errcode'] == 401:
                data = {"errcode": 401, "mess": "saltapi 认证失败"}
            else:
                data = {"errcode": 500, "mess": "saltapi获取token,expire失败"}

        except BaseException as e:
            data = {"errcode": 500, "mess": "saltapi获取token异常，%s"%e}
        return data


    def _handle_get_salt_host_list(self,s_type):
        '''
        处理获取salt获取主机信息
        :param s_type: 类型
        :return:
        '''
        params = {'client': 'wheel', 'fun': 'key.list_all'}
        send_data = json.dumps(params)

        try:
            request = requests.post(self.url, data=send_data, headers=self.headers, verify=False,timeout=10)
            status_code = request.status_code
            if status_code == 200:
                response = request.json()
                result = dict(response)
                if s_type == 'accepted':
                    res = result['return'][0]['data']['return']['minions']
                elif s_type == 'unaccepted':
                    res = result['return'][0]['data']['return']['minions_pre']
                elif s_type == 'rejected':
                    res = result['return'][0]['data']['return']['minions_rejected']
                elif s_type == 'denied':
                    res = result['return'][0]['data']['return']['minions_denied']
                else:
                    res = []
                data = {"errcode": 200, "mess": res}

            elif status_code == 401:
                data = {"errcode": 401, "mess": "saltapi 认证失败"}

            else:
                data = {"errcode": 403, "mess": "调用获取minionkey状态码返回未知"}

        except BaseException as e:
            data = {"errcode": 500, "mess": "调用minionkey异常，异常消息为：%s" % e}


        return data


    def _handle_add_salt_auth(self,s_type):
        '''
        添加认证主机到数据库
        :param s_type: 认证类型
        :return:响应体
        '''

        res = self._handle_get_salt_host_list(s_type)
        if res['errcode'] == 200:
            saltmaster = SaltMaster.objects.filter(saltm_status=1).first()
            if saltmaster is not None:
                salt_master_id = saltmaster.id
                newHostList=res['mess']
                if len(newHostList) !=0:
                    created_hosts = []
                    try:
                            for host in newHostList:
                                existing_host = SaltAuth.objects.filter(
                                    saltname=host,
                                    salt_master=salt_master_id
                                ).count()
                                if existing_host ==0:
                                    SaltAuth.objects.create(saltname=host,salt_status=1,salt_isvalidate=0,salt_bind_status=0,salt_master=salt_master_id)
                                    created_hosts.append(host)
                                else:
                                    created_hosts.append(host)
                            if created_hosts:
                                data = {"errcode": 200, "mess": "获取最新主机成功：%s" %created_hosts}
                            else:
                                data = {"errcode": 200, "mess": "已获取最新主机，无新服务器添加"}
                    except BaseException as e:
                        data={"errcode": 404, "mess": "添加主机信息异常：%s"%e}
                else:
                    data = {"errcode": 200, "mess": "【%s】没有可添加服务器"%saltmaster}
            else:
                data = {"errcode": 404, "mess": "没有可用的saltmaster"}
        else:
            data = res
        return data

    def _handle_salt_client_auth(self,salt_client,s_type):
        '''
        处理salt-minion客户端 认证
        :param salt_client:
        :return:
        '''
        try:
            saltauth = SaltAuth.objects.get(saltname=salt_client, salt_status=1,salt_bind_status=0)
        except SaltAuth.DoesNotExist:
            data = {"errcode": 404, "mess": "【%s】已经被绑定状态无法执行删除"%salt_client}
            return data
        salt_client = saltauth.saltname

        # 接受
        if s_type == 'accept':

            params = {'client': 'wheel', 'fun': 'key.accept', 'match': salt_client}
        #删除
        elif s_type == 'delete':

            params = {'client': 'wheel', 'fun': 'key.delete', 'match': salt_client}
        #拒绝
        elif s_type == 'rejecte':

            params = {'client': 'wheel', 'fun': 'key.reject', 'match': salt_client}
        else:

            data = {"errcode": 404, "mess": "未知认证类型"}
            return data
        result = self._get_data(self.url,params)

        if result['errcode'] == 200:

            res = result['mess']['data']['success']
            if res:

                if s_type == 'accept':
                    SaltAuth.objects.filter(saltname=salt_client).update(salt_isvalidate=1)
                    #添加主机与salt-minion绑定
                    data = self._add_salt_host_bind(salt_client)
                    return data
                elif  s_type == 'delete':
                    SaltAuth.objects.filter(saltname=salt_client).update(salt_isvalidate=3,salt_status=0,salt_bind_status=0)
                elif  s_type == 'rejecte':
                    SaltAuth.objects.filter(saltname=salt_client).update(salt_isvalidate=2)
                else:
                    data = {"errcode": 404, "mess": "未知认证类型"}
                    return data

                data = {"errcode": 200, "mess":"主机【%s】【%s】成功"%(salt_client,s_type)}

            else:
                data = {"errcode": 404, "mess":"主机【%s】【%s】认证失败"%(salt_client,s_type)}
            return data
        else:
            return result



    def _get_data(self, url,params):
        '''
        通用处理saltapi 请求
        :param url: 请求地址
        :param params: 请求参数
        :return:响应体
        '''


        try:

            request = requests.post(url=url, data=json.dumps(params), headers=self.headers, verify=False,timeout=30)
            status_code = request.status_code
            if status_code == 200:

                response = request.json()
                result = dict(response)
                data = {"errcode": 200, "mess": result['return'][0] }
            elif status_code == 401:
                data = {"errcode": 401, "mess": "saltapi 认证失败"}
            else:
                data = {"errcode": 403, "mess": "调用saltapi返回未知状态码"}

        except BaseException as e:
            data = {"errcode": 500, "mess": "请求saltapi接口异常：%s" % e}
        mess='请求saltapi接口返回结果：%s'%data
        logger.info(mess)
        return data

    def _handle_salt_execute_command(self, tgt, fun,client_type='local',arg=None):
        '''
        远程执行命令
        :param tgt: salt-minon客户端
        :param method:执行方法
        :param arg:参数
        :return: 返回命令执行结果
        '''

        if arg:
            params = {'client': client_type, 'fun': fun, 'tgt': tgt, 'arg': arg}
        else:
            params = {'client': client_type, 'fun': fun, 'tgt': tgt}
        mess = 'saltapi远程执行命令命令参数:%s ' % params
        logger.info(mess)
        result = self._get_data(self.url,params)
        return result


    def _look_jid(self, jid):
        '''
        根据异步执行命令返回的jid查看事件结果
        :param jid:
        :return:
        '''
        params = {'client': 'runner', 'fun': 'jobs.lookup_jid', 'jid': jid}
        result = self.get_data(params)
        if result['errcode'] == 200:
            return result
        else:
            return result

    def _add_salt_host_bind(self,tgt):
        '''
        添加主机与salt-minion 绑定
        :param tgt: salt-minion客户端
        :return: 响应体
        '''
        time.sleep(5)
        fun = 'test.ping'
        arg = ''
        result = self._handle_salt_execute_command(tgt, fun, arg=arg)
        if result['errcode'] == 200 and result['mess'][tgt]:

            fun='grains.get'
            arg = 'os'
            result = self._handle_salt_execute_command(tgt,fun,arg=arg)
            if result['errcode'] == 200:
                system=result['mess'][tgt]
                if system == 'CentOS':
                    host_system = 0
                elif system == 'Windows':
                    host_system = 1
                else:
                    host_system = 2
            else:
                host_system = 2

            fun = 'grains.get'
            arg = 'ipv4'
            result = self._handle_salt_execute_command(tgt, fun, arg=arg)
            if result['errcode'] == 200:
                host_lan = result['mess'][tgt][-1]
            else:
                host_lan = '127.0.0.1'

            host_num = Host.objects.filter(name=tgt,status=1).count()
            if host_num ==0:
                try:
                    saltauth = SaltAuth.objects.get(saltname=tgt)  # 获取唯一匹配的对象

                    host = Host(name=tgt, salt_id=saltauth.id, lan=host_lan, wlan=host_lan, system=host_system, status=1)
                    host.save()
                    SaltAuth.objects.filter(saltname=tgt).update(salt_bind_status=1)
                except SaltAuth.DoesNotExist:
                    data = {"errcode": 404, "mess": "未找到相应的Salt认证信息"}
                    return data
                except BaseException as e:
                    data = {"errcode": 404, "mess": "添加主机异常：%s" % e}
                    return data
                data = {"errcode": 200, "mess": "添加主机[%s]成功" %host.name}
            else:
                data = {"errcode": 404, "mess": "已存在【%s】主机"%tgt}


        else:
            data = {"errcode": 404, "mess": "请检查salt-minion【%s】启动状态" % tgt}

        return data








